﻿using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Workflow.Runtime;

namespace Algoritmos_de_Ordenação
{
    public enum SortStatus
    {
        Paused,
        Stopped,
        Unsorted,
        Sorting,
        Sorted
    }

    
    public class DrawingRegion : Canvas
    {
        protected int numElemArray = 45;
        protected const int maxItemHeight = 60;
        protected const int ItemMultiplier = 5;
        protected Thread sortThread;
        protected int[] numberArray;
        protected int[] arrayInicial;
        int comparisonsNumber = 0;
        int swapsNumber = 0;
        protected ListBox propertyBox;
        protected bool sorted;
        int velocity = 0;
        protected int arraySeed;
        static AutoResetEvent waitHandle = new AutoResetEvent(false);

        bool isCompareAllTab = false;

        protected ArrayList bars = new ArrayList();
        private Line greenBar = new Line();
        Label me = new Label();
        TabType tabType;

        StackPanel spCSharp = new StackPanel();
        StackPanel spCPlusPlus = new StackPanel();

        RichTextBox rtbCSharp = new RichTextBox();
        RichTextBox rtbCPlusPlus = new RichTextBox();

        bool showExecTrace = true;

        static int sortedArrays = 0;
        public bool Sorted
        {
            get { return sorted; }
            set { this.sorted = value; }
        }
        
        public int SortSpeed
        {
            get {return this.velocity; }
            set { this.velocity = value; }
        }
        
        public int ArraySeed
        {
            get { return this.arraySeed; }
            set { this.arraySeed = value; }
        }


        public int newWidth
        {
            set
            {
                this.Width = value;
                this.me.Width = value;
            }
        }

        public bool isCompareAll
        {
            get { return this.isCompareAllTab; }
            set { this.isCompareAllTab = value; }
        }

        protected void wf()
        {
            // Start the workflow runtime engine.
            using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
            {
                workflowRuntime.StartRuntime();

                // Listen for the workflow events
                workflowRuntime.WorkflowCompleted += OnWorkflowCompleted;

                waitHandle.WaitOne(this.velocity);

                // Stop the workflow runtime engine.
                workflowRuntime.StopRuntime();
            }
        }

        // This method will be called when a workflow instance is completed
        // waitHandle is set so the main thread can continue
         void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
        {
            waitHandle.Set();
        }

        public void updateNumberArraySize(int x)
        {
            this.numElemArray = x;
                       
            this.numberArray = new int[x];
            this.arrayInicial = new int[x];
            createBars();
        }
        
        public DrawingRegion()
        {

        }
        
        public DrawingRegion(TabItem tabItem, TabType tt)
        {
            this.tabType = tt;
            Canvas.SetLeft(this, 6);
            Canvas.SetTop(this, 26);
                        
            this.Height = 325;
            
            this.Background = Brushes.Transparent;
            this.Width = 532;

            me.Height = this.Height + 4;
            me.Width = this.Width;
            me.BorderBrush = Brushes.LightGray;
            me.BorderThickness = new Thickness(0.5);
            
            Canvas.SetLeft(me, 0);
            Canvas.SetTop(me, 0);
            this.Children.Add(me);
            this.sorted = false;
            this.numberArray = new int[numElemArray];
            this.arrayInicial = new int[numElemArray];
            this.velocity = (int) SpeedClicked.Normal;
            createBars();
            createGBContent(tabType);
            raiseIsSortedFlagThread();
            updateInformationsThread(SortStatus.Unsorted, 0, 0);            
            this.arraySeed = 0;
        }

        private void createBars()
        {
            FillArray();

            foreach (Line b in bars)
            {
                this.Children.Remove(b);
            }

            bars.Clear();

            for (int i = 0; i < numElemArray; i++)
            {
                Line b = new Line();
                bars.Add(b);
                this.Children.Add(b);
            }

            drawBars(Brushes.Black);

            greenBar = new Line();
            greenBar.Stroke = Brushes.Green;
            greenBar.StrokeThickness = 6;
            greenBar.Y2 = this.Height - 2;
            greenBar.Visibility = Visibility.Hidden;
            this.Children.Add(greenBar);
        }
        
        private void loadRTF(TabType tabType)
        {
            switch(tabType)
            {
                case TabType.Bubble:
                    loadRTF(Properties.Resources.BubbleC_, rtbCSharp);
                    loadRTF(Properties.Resources.BubbleC__, rtbCPlusPlus);
                    break;
                case TabType.Heap:
                    loadRTF(Properties.Resources.HeapC_, rtbCSharp);
                    loadRTF(Properties.Resources.HeapC__, rtbCPlusPlus);
                    break;
                case TabType.Insertion:
                    loadRTF(Properties.Resources.InsertionC_, rtbCSharp);
                    loadRTF(Properties.Resources.InsertionC__, rtbCPlusPlus);
                    break;
                case TabType.Quick:
                    loadRTF(Properties.Resources.QuickC_, rtbCSharp);
                    loadRTF(Properties.Resources.QuickC__, rtbCPlusPlus);
                    break;
                case TabType.QuickRandom:
                    loadRTF(Properties.Resources.Random_QuickC_, rtbCSharp);
                    loadRTF(Properties.Resources.Random_QuickC__, rtbCPlusPlus);
                    break;
                case TabType.Selection:
                    loadRTF(Properties.Resources.SelectionC_, rtbCSharp);
                    loadRTF(Properties.Resources.SelectionC__, rtbCPlusPlus);
                    break;
                case TabType.Shell:
                    loadRTF(Properties.Resources.ShellC_, rtbCSharp);
                    loadRTF(Properties.Resources.ShellC__, rtbCPlusPlus);
                    break;
            }

        }

        private void loadRTF(string filename, RichTextBox rtb)
        {
            ASCIIEncoding encoding = new ASCIIEncoding();
            byte[] bytes = encoding.GetBytes(filename);

            using (MemoryStream s = new MemoryStream(bytes))
            {
                TextRange range = new TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd);
                range.Load(s, DataFormats.Rtf);
            }
        }
        
        public bool showTrace
        {
            get { return this.showExecTrace; }
            set { this.showExecTrace = value; }
        }

        private void highlightLine(RichTextBox rtb, int lineToHighlight)
        {
            Block[] b = new Block[rtb.Document.Blocks.Count];

            rtb.Document.Blocks.CopyTo(b, 0);

            foreach (Block blc in b)
            {
                blc.Background = Brushes.Black;
            }

            if (!(lineToHighlight == -1))
            {
                b[lineToHighlight].Background = Brushes.Yellow;
                rtb.ScrollToHome();
                for (int i = 0; i < lineToHighlight; i++)
                {
                    rtb.LineDown();
                }
            }
            else rtb.ScrollToHome();
        }

        private void setRtbProperties(RichTextBox rtb)
        {
            rtb.Background = Brushes.Black;
            rtb.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
            rtb.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
            rtb.IsReadOnly = true;
            rtb.Height = 220;
            rtb.Document.MinPageWidth = 650;            
        }

        private void createGBContent(TabType tabType)
        {
            loadRTF(tabType);

            setRtbProperties(rtbCSharp);
            setRtbProperties(rtbCPlusPlus);

            spCSharp.Children.Add(rtbCSharp);
            spCPlusPlus.Children.Add(rtbCPlusPlus);
        }

        protected delegate void updateGBDelegate(int lineToHighlight);
        protected void updateGB(int lineToHighlight)
        {
            Canvas myParent = (Canvas)this.Parent;

            highlightLine(rtbCSharp, lineToHighlight);
            highlightLine(rtbCPlusPlus, lineToHighlight);

            ((GroupBox)myParent.Children[3]).Content = spCSharp;
            ((GroupBox)myParent.Children[4]).Content = spCPlusPlus;
        }

        public void updateGBThread(int lineToHighlight)
        {
            if (isCompareAll)
                return;
            
            if (!showTrace)
            {
                lineToHighlight = -1;
            }
            this.Dispatcher.BeginInvoke(new updateGBDelegate(updateGB), new object[]{lineToHighlight});
        }

        protected delegate void raiseIsSortedFlagDelegate();
        protected void raiseIsSortedFlag()
        {
            Canvas myParent = (Canvas)this.Parent;

            if (myParent == null)
            {
                return;
            }

            if (isCompareAll)
            {
                if (sortedArrays < 6)
                {
                    sortedArrays++;
                    return;
                }
            }
            {
                Canvas myBrotherContent = (Canvas)((GroupBox)myParent.Children[2]).Content;

                ((Button)myBrotherContent.Children[4]).RaiseEvent(new RoutedEventArgs(Button.ClickEvent));

                sortedArrays = 0;
            }
            
        }

        protected void raiseIsSortedFlagThread()
        {
            this.Dispatcher.BeginInvoke(new raiseIsSortedFlagDelegate(raiseIsSortedFlag));
        }
        
        private void FillArray()
        {
            Random randomNumber;
            int seed;
            int i;
            
            if (this.arraySeed == 0)
            {
                seed = unchecked((int)DateTime.Now.Ticks) + (int)tabType;
            }
            else
            {
                seed = arraySeed;
            }

            randomNumber = new Random(seed);
            for (i = 0; i < numElemArray; i++)
            {
                this.numberArray[i] =
                    (ItemMultiplier * randomNumber.Next(1, maxItemHeight));
            }

            inicializa();
        }

        public void Stop()
        {
            updateLineVisibilityThread(greenBar, Visibility.Hidden);
            
            if ((sortThread != null) && (sortThread.IsAlive))
            {
                if (sortThread.ThreadState==ThreadState.Suspended)
                {
                    sortThread.Resume();
                }
                sortThread.Abort();
                
                raiseIsSortedFlagThread();
                drawBars(Brushes.Black);
                updateInformationsThread(SortStatus.Unsorted, 0, 0);
                return;
            }
            
            if ((sortThread != null) && (!sortThread.IsAlive))
                if (this.sorted)
                {
                    drawBars(Brushes.Gray);
                    updateInformationsThread(SortStatus.Sorted, comparisonsNumber, swapsNumber);
                }
                else
                {
                    drawBars(Brushes.Black);
                    
                    updateInformationsThread(SortStatus.Unsorted, 0, 0);
                }
                
        }

        public void Pausa()
        {
            if ((sortThread != null) && (sortThread.IsAlive))
            {
                sortThread.Suspend();
            }
        }
        
        public void Reset()
        {
            fillArrayWithInitial();
            
            updateInformationsThread(SortStatus.Unsorted, 0, 0);
            drawBars(Brushes.MediumSeaGreen);
        }

        public void randomica()
        {
            this.sorted = false;
            FillArray();
            updateInformationsThread(SortStatus.Unsorted, 0, 0);
        }

        public void Reinicia()
        {
            if ((sortThread != null)&& (sortThread.ThreadState==ThreadState.Suspended))
            {
                sortThread.Resume();
            }
        }

        public void Sort()
        {
            if (((sortThread != null) && sortThread.IsAlive))
            {
                //sortThread.Abort();
            }
            else
            {
                updateInformationsThread(SortStatus.Sorting, 0, 0);
                this.sortThread = new Thread(new ThreadStart(DoSort));
                this.sortThread.Start();
            }
        }


        protected delegate void updateInformationsDelegate(SortStatus ss, int comparisons, int swaps);
        protected void updateInformations(SortStatus ss, int comparisons, int swaps)
        {
            this.Sorted = false;
            this.comparisonsNumber = comparisons;
            this.swapsNumber = swaps;

            Canvas myParent = (Canvas)this.Parent;
            if (myParent==null)
            {
                return;
            }

            string temp = "Tipo: " + this.tabType.ToString() + " Sort\n";
            if (tabType.Equals(TabType.QuickRandom))
            {
                temp = "Tipo: Random Quick Sort\n";
            }

            switch(ss)
            {
                case SortStatus.Paused: temp += "Status: Pausado\n";
                    break;
                case SortStatus.Sorted: temp += "Status: Ordenado\n";
                    this.Sorted = true;
                    break;
                case SortStatus.Sorting: temp += "Status: Ordenando\n";
                    break;
                case SortStatus.Stopped: temp += "Status: Parado\n";
                    break;
                case SortStatus.Unsorted: temp += "Status: Desordenado\n";
                    break;
            }

            temp += "Comparações: " + comparisons.ToString() + "\n";
            temp += "Trocas: " + swaps.ToString();

            if (!isCompareAll)
                ((TextBox)myParent.Children[5]).Text = temp;
            else ((TextBox)myParent.Children[(int)tabType * 2 + 5]).Text = temp;

            
            
        }

        protected void updateInformationsThread(SortStatus ss, int comparisons, int swaps)
        {
            this.Dispatcher.BeginInvoke(new updateInformationsDelegate(updateInformations), new object[] { ss, comparisons, swaps });
        }
        
        protected virtual void DoSort()
        {
        }
        
        public void desenhaBarras()
        {
            if (this.Sorted)
            {
                this.drawBars(Brushes.Black);
            }
            else
            {
                this.drawBars(Brushes.Red);
            }
        }

        protected delegate void updateBarsDelegate(Line l, Brush strokeColor, int index);
        protected void updateBars(Line l, Brush strokeColor, int index)
        {
            l.Stroke = strokeColor;
            l.X1 = 11 * index + 23;
            l.X2 = 11 * index + 23;
            l.Y1 = this.Height - numberArray[index] - 4;
        }

        protected void updateBarsThread(Brush color)
        {
            foreach (Line b in bars)
            {
                this.Dispatcher.BeginInvoke(new updateBarsDelegate(updateBars), new object[] { b, color, bars.IndexOf(b) });                
            }
        }

        protected void drawBars(Brush color)
        {
            foreach ( Line b in bars)
            {
                b.Stroke = color;
                b.StrokeThickness = 6;
                b.X1 = 11 * bars.IndexOf(b) + 23;
                b.X2 = 11 * bars.IndexOf(b) + 23;
                b.Y1 = this.Height - numberArray[bars.IndexOf(b)] - 4;
                b.Y2 = this.Height - 2;
            }
        }

        protected delegate void updateLineVisibilityDelegate(Line l, Visibility vis);
        protected void updateLineVisibility(Line l, Visibility vis)
        {
            l.Visibility = vis;
        }

        protected void updateLineVisibilityThread(Line b, Visibility vis)
        {
            this.Dispatcher.BeginInvoke(new updateLineVisibilityDelegate(updateLineVisibility), new object[] { b, vis });
        }

        protected delegate void updateLineHeightDelegate(Line l, int index, int newY1);
        protected void updateLineHeight(Line l, int index, int newY1)
        {
            l.X1 = 11 * index + 23;
            l.X2 = 11 * index + 23;
            l.Y1 = this.Height - newY1 - 4; ;
        }

        protected void updateLineHeightThread(Line b, int index, int newY1)
        {
            this.Dispatcher.BeginInvoke(new updateLineHeightDelegate(updateLineHeight), new object[] { b, index, newY1 });
        }

        protected delegate void compareTwoDelegate(Line l, Brush strokeColor);
        protected void compareTwo(Line l, Brush strokeColor)
        {
            l.Stroke = strokeColor;
        }

        protected void compareTwoThread(int bar1, int bar2)
        {
            foreach (Line b in bars)
            {
                Brush color = Brushes.Red;
                
                if ((bars.IndexOf(b) == bar1) || (bars.IndexOf(b) == bar2))
                    color = Brushes.Blue;
                                
                this.Dispatcher.BeginInvoke(new compareTwoDelegate(compareTwo), new object[] { b, color });
            }            
        }

        protected void compareTwoWithFixedThread ( int bar1, int bar2, int barFixed, int value )
        {
            foreach ( Line b in bars )
            {
                Brush color = Brushes.Red;

                if ((bars.IndexOf(b) == bar1) || (bars.IndexOf(b) == bar2) || (bars.IndexOf(b) == barFixed))
                {
                    color = Brushes.Blue;
                }

                if ((bars.IndexOf(b) == barFixed))
                {
                    updateLineVisibilityThread(greenBar, Visibility.Visible);
                    updateLineHeightThread(greenBar, bars.IndexOf(b), value);
                }

                updateLineHeightThread(b, bars.IndexOf(b), numberArray[bars.IndexOf(b)]);

                this.Dispatcher.BeginInvoke ( new compareTwoDelegate ( compareTwo ), new object[] { b, color } );
            }

        }

        protected delegate void swapTwoDelegate(Line bar1, Line bar2);
        protected void swapTwo(Line bar1, Line bar2)
        {
            double y1 = bar1.Y1;

            bar1.Y1 = bar2.Y1;
            bar2.Y1 = y1;            
        }
        protected void swapTwoThread(int bar1, int bar2)
        {            
            this.Dispatcher.BeginInvoke(new swapTwoDelegate(swapTwo), new object[] { ((Line)bars[bar1]), ((Line)bars[bar2]) });
        }

        protected delegate void swapTwoWithFixedDelegate(Line bar1, Line barFixed);
        protected void swapTwoWithFixed(Line bar1, Line barFixed)
        {
            double y1 = bar1.Y1;

            bar1.Y1 = greenBar.Y1;
        }
        protected void swapTwoWithFixedThread(int bar1, int barFixed)
        {
            this.Dispatcher.BeginInvoke(new swapTwoWithFixedDelegate(swapTwoWithFixed), new object[] { ((Line)bars[bar1]), ((Line)bars[barFixed]) });
        }
        
        protected void endSorting(int comparisons, int swaps)
        {
            updateInformationsThread(SortStatus.Sorted, comparisons, swaps);
            raiseIsSortedFlagThread();
            updateBarsThread(Brushes.Gray);
            updateLineVisibilityThread(greenBar, Visibility.Hidden);
            updateGBThread(-1);
        }

        internal void fillArrayOrdenado()
        {
            for (int i = 0; i < numElemArray; i++)
            {
                this.numberArray[i] = (int)(i * ItemMultiplier * 1.3) + 5;
            }
            inicializa();
        }
        
        internal void fillArrayWithInitial()
        {
            for (int i = 0; i < numElemArray; i++)
            {
                this.numberArray[i] = this.arrayInicial[i];
            }
        }

        internal void fillArrayInverso()
        {
            int cont = 0;
            for (int i = numElemArray; i > 0; i--)
            {
                this.numberArray[cont] = (int)(i * ItemMultiplier * 1.3) + 5;
                cont++;
            }

            inicializa();
        }
        
        void inicializa()
        {
            for (int i = 0; i < numElemArray; i++)
            {
                this.arrayInicial[i] = this.numberArray[i];
            }
            
            this.sorted = false;
            updateInformationsThread(SortStatus.Unsorted, 0, 0);
        }
    }
}
